Skip to content

MSC4140: put delay_id in unsigned data for sender#19479

Merged
reivilibre merged 10 commits intoelement-hq:developfrom
AndrewFerr:msc4140-delay_id-in-sync
Mar 16, 2026
Merged

MSC4140: put delay_id in unsigned data for sender#19479
reivilibre merged 10 commits intoelement-hq:developfrom
AndrewFerr:msc4140-delay_id-in-sync

Conversation

@AndrewFerr
Copy link
Copy Markdown
Member

Implements matrix-org/matrix-spec-proposals@49b200d

Pull Request Checklist

  • Pull request is based on the develop branch
  • Pull request includes a changelog file. The entry should:
    • Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from EventStore to EventWorkerStore.".
    • Use markdown where necessary, mostly for code blocks.
    • End with either a period (.) or an exclamation mark (!).
    • Start with a capital letter.
    • Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry.
  • Code style is correct (run the linters)

When persisting a delayed event to the timeline, include its delay_id in
the event's unsigned section in /sync responses to the event sender.
@AndrewFerr AndrewFerr requested a review from a team as a code owner February 18, 2026 16:46
@reivilibre reivilibre self-requested a review March 3, 2026 12:13
Copy link
Copy Markdown
Contributor

@reivilibre reivilibre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable overall


txn_id: str
"""The transaction ID, if it was set when the event was created."""
delay_id: str
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure everything else in this file does it correctly right now, but I would be in favour of making this optional

Suggested change
delay_id: str
delay_id: str | None

This will also involve changing the Rust part to return Nones when needed.


I'm not dead-set on this, so open to your thoughts? But it seems like it is more typechecker-friendly this way.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My vote is to leave this as it is for now, as much as I appreciate proper typing, because there appears to be some nuance to this that I don't fully grasp at the moment & am hesitant to muck around with.

From what I can gather from the Rust code, there are some fields in the metadata object that are always present but may set to None (like stream_ordering and instance_name), and others that may be absent but must always be non-None when present (like txn_id and device_id).

So, it looks like there is a functional difference between the not-typed-as-optional fields that Python code looks up with getattr, and ones that are typed as optional. Since delay_id is used in much the same way as txn_id, IMO it's sufficient for the time being to use the same code patterns of the latter for the former.

# unsigned section of the event if the event was sent by the same session as the one
# requesting the event.
txn_id: str | None = getattr(e.internal_metadata, "txn_id", None)
delay_id: str | None = getattr(e.internal_metadata, "delay_id", None)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(we could then avoid this getattr call if we did make it natively str | None)

# requesting the event.
txn_id: str | None = getattr(e.internal_metadata, "txn_id", None)
delay_id: str | None = getattr(e.internal_metadata, "delay_id", None)
if (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arguably this should have been done earlier, but now we're adding more things that use this logic, it feels even stronger that it would be worth pulling out a _should_include_same_session_metadata() -> bool
function (or something along those lines) that would let us isolate the condition logic from the actual addition of the fields.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of the condition is the presence of the fields to be added to the event, which IMO makes it not worth splitting this out into a standalone function, lest it just moves the data/logic coupling somewhere else rather than decoupling them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not necessarily standalone.

I guess what I was going for was to replace the if-else block starting R483 with one that, instead of setting the fields directly, sets should_include_same_session_metadata = True and have a single block to populate that metadata afterwards (i.e. separate condition from action, also meaning we don't have to write the action twice).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With d699205, the code that adds delay_id is pulled out from the pre-existing code and is much simpler than before, so IMO there's less of a need to refactor the code for adding the transaction_id.

774e1ff applies some refactorings that I felt are now appropriate considering the new changes.

@AndrewFerr AndrewFerr requested a review from reivilibre March 4, 2026 06:40
Copy link
Copy Markdown
Contributor

@reivilibre reivilibre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good. Some MSC divergences I spotted though, I think.

Comment on lines +497 to +498
if delay_id is not None:
d["unsigned"]["delay_id"] = delay_id
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm re-reading the MSC now and noticing that the MSC says we should include the delay_id to the event sender, but here we only include when the person is using the exact same device.

Ref: https://github.com/matrix-org/matrix-spec-proposals/blob/1e527c3d880135a4975b56f700adf007e2914565/proposals/4140-delayed-events-futures.md#L296


Which one is correct?


The MSC also says it's only included on /sync (same ref), yet at first glance it seems like this is included on any event-returning CSAPI request.

Copy link
Copy Markdown
Member Author

@AndrewFerr AndrewFerr Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm re-reading the MSC now and noticing that the MSC says we should include the delay_id to the event sender, but here we only include when the person is using the exact same device. ... Which one is correct?

It should be included for all sessions. Will write a change for this.

The MSC also says it's only included on /sync (same ref), yet at first glance it seems like this is included on any event-returning CSAPI request.

Ah, I had put this code here to do the same as what's done for including the transaction_id in an event, thinking that it only came through /synced events as well. Does the transaction_id get included in any event-returning request? If so, then I'd argue for delay_id to follow suit, and would adjust the MSC accordingly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, including delay_id for more than just /sync would definitely be useful, regardless of what is done for transaction_id (or any other unsigned data, for that matter). MSC discussion is here: matrix-org/matrix-spec-proposals#4140 (comment)

As for including delay_id for all sessions, see d699205

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record, the MSC discussion settled on including delay_id in unsigned data for all requests by the event's sender, not just /sync.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I think returning metadata differently in different endpoints would be a strange choice because it makes it hard for clients to be able to reliably cache events.

Check for requester == sender before looking up keys to include in the
unsigned data. Do this in an if branch instead of storing the check to
a variable, because otherwise mypy doesn't know that the requester is
not None.
@AndrewFerr AndrewFerr requested a review from reivilibre March 5, 2026 17:16
Copy link
Copy Markdown
Contributor

@reivilibre reivilibre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@reivilibre reivilibre merged commit c0924fb into element-hq:develop Mar 16, 2026
41 checks passed
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Mar 24, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [element-hq/synapse](https://github.com/element-hq/synapse) | minor | `v1.149.1` → `v1.150.0` |

---

### Release Notes

<details>
<summary>element-hq/synapse (element-hq/synapse)</summary>

### [`v1.150.0`](https://github.com/element-hq/synapse/releases/tag/v1.150.0)

[Compare Source](element-hq/synapse@v1.149.1...v1.150.0)

### Synapse 1.150.0 (2026-03-24)

No significant changes since 1.150.0rc1.

### Synapse 1.150.0rc1 (2026-03-17)

#### Features

- Add experimental support for the [MSC4370](matrix-org/matrix-spec-proposals#4370) Federation API `GET /extremities` endpoint. ([#&#8203;19314](element-hq/synapse#19314))
- [MSC4140: Cancellable delayed events](matrix-org/matrix-spec-proposals#4140): When persisting a delayed event to the timeline, include its `delay_id` in the event's `unsigned` section in `/sync` responses to the event sender. ([#&#8203;19479](element-hq/synapse#19479))
- Expose [MSC4354 Sticky Events](matrix-org/matrix-spec-proposals#4354) over the legacy (v3) /sync API. ([#&#8203;19487](element-hq/synapse#19487))
- When Matrix Authentication Service (MAS) integration is enabled, allow MAS to set the user locked status in Synapse. ([#&#8203;19554](element-hq/synapse#19554))

#### Bugfixes

- Fix `Build and push complement image` CI job pointing to non-existent image. ([#&#8203;19523](element-hq/synapse#19523))
- Fix a bug introduced in v1.26.0 that caused deactivated, erased users to not be removed from the user directory. ([#&#8203;19542](element-hq/synapse#19542))

#### Improved Documentation

- In the Admin API documentation, always express path parameters as `/<param>` instead of as `/$param`. ([#&#8203;19307](element-hq/synapse#19307))
- Update docs to clarify `outbound_federation_restricted_to` can also be used with the [Secure Border Gateway (SBG)](https://element.io/en/server-suite/secure-border-gateways). ([#&#8203;19517](element-hq/synapse#19517))
- Unify Complement developer docs. ([#&#8203;19518](element-hq/synapse#19518))

#### Internal Changes

- Put membership updates in a background resumable task when changing the avatar or the display name. ([#&#8203;19311](element-hq/synapse#19311))
- Add in-repo Complement test to sanity check Synapse version matches git checkout (testing what we think we are). ([#&#8203;19476](element-hq/synapse#19476))
- Migrate `dev` dependencies to [PEP 735](https://peps.python.org/pep-0735/) dependency groups. ([#&#8203;19490](element-hq/synapse#19490))
- Remove the optional `systemd-python` dependency and the `systemd` extra on the `synapse` package. ([#&#8203;19491](element-hq/synapse#19491))
- Avoid re-computing the event ID when cloning events. ([#&#8203;19527](element-hq/synapse#19527))
- Allow caching of the `/versions` and `/auth_metadata` public endpoints. ([#&#8203;19530](element-hq/synapse#19530))
- Add a few labels to the number groupings in the `Processed request` logs. ([#&#8203;19548](element-hq/synapse#19548))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My44NC4yIiwidXBkYXRlZEluVmVyIjoiNDMuODQuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/5040
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants